import 'package:flutter/material.dart';
import 'dart:async';

abstract class LoadingTable<B> extends StatefulWidget {

  LoadingTable({Key key, this.name, this.title}) : super(key : key);

  String name;
  String title;

  @override
  _LoadingTableState createState() => _LoadingTableState();

  Future<List<B>> loadingData();

  Widget addData() {
    return Scaffold(
        appBar: new AppBar(
            title: const Text("add data form!")
        ),
        body: const Center(
          child: const Text("add data from!"),
        )
    );
  }

  Widget editData(TableBeanVo tableBeanVo) {
    return Scaffold(
        appBar: new AppBar(
            title: const Text("eidt data form!")
        ),
        body: const Center(
          child: const Text("eidt data from!"),
        )
    );
  }

  void setData(List<B> data) {
    List<TableBeanVo> tableBeanVoList = data.map((b) {
      TableBeanVo vo = new TableBeanVo(b, false);
      return vo;
    }).toList();
  }

  List<Widget> celles(B bean);

  List<SortDataColumn> sortDataColumns();

}

class _LoadingTableState extends State<LoadingTable> {
  int _rowsPerPage = PaginatedDataTable.defaultRowsPerPage;
  int _sortColumnIndex;
  bool _sortAscending = false;
  LoadingTableDataSource _dessertsDataSource;
  bool isLoaded = false;

  Future<List> _futureData;

  void _sort<T>(Comparable<T> getField(TableBeanVo d), int columnIndex, bool ascending) {
    _dessertsDataSource._sort<T>(getField, ascending);
    setState(() {
      _sortColumnIndex = columnIndex;
      _sortAscending = ascending;
    });
  }

  @override
  void initState() {
    super.initState();
    _dessertsDataSource = new LoadingTableDataSource(widget, new List<TableBeanVo>());
    _futureData = widget.loadingData();

    _futureData.then((list){
      List<TableBeanVo> _newDatas = new List();
      for(var data in list) {
        TableBeanVo beanTableVo = new TableBeanVo(data, false);
        _newDatas.add(beanTableVo);
      }
      _addData(_newDatas);
    });
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(title: new Text(widget.title)),
      body:_doBody(),
    );
  }

  Widget _doBody() {
    return FutureBuilder(
      future: _futureData,
      builder: (BuildContext context, AsyncSnapshot<List> snapshot) {
        switch (snapshot.connectionState) {
          case ConnectionState.none: return new Text('Press button to start');
          case ConnectionState.waiting: return _buildIndicator();
          default:
            if (snapshot.hasError)
              return _buildError();
            else
              return _buildTable();
        }
      },
    );
  }

  Widget _buildError() {
    return new Center(
      child: new Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          new Text("加载失败!!!", style: const TextStyle(color: Colors.blueAccent, fontSize: 26.0),),
          new Icon(Icons.error, color: Colors.blueAccent, size: 56.0,),
        ],
      ),
    );
  }

  Widget _buildIndicator() {
    return new Center(
        child: new CircularProgressIndicator(),
    );
  }

  Widget _buildTable() {
    return new ListView(
        padding: const EdgeInsets.all(20.0),
        children: <Widget>[
          new PaginatedDataTable(
              header: new Text('${r'${'}widget.name}'),
//              rowsPerPage: _rowsPerPage,
//              onRowsPerPageChanged: (int value) { setState(() { _rowsPerPage = value; }); },
//              sortColumnIndex: _sortColumnIndex,
//              sortAscending: _sortAscending,
              onSelectAll: _dessertsDataSource._selectAll,
              columns: widget.sortDataColumns().map<DataColumn>((sortDataColumn) {
                return new DataColumn(
                    label: sortDataColumn.label,
                    tooltip: sortDataColumn.tooltip??"",
                    numeric: sortDataColumn.numeric??false,
                    onSort: (int columnIndex, bool ascending) => _sort(sortDataColumn.getField, columnIndex, ascending)
                );
              }).toList(),
              source: _dessertsDataSource,
              actions: <Widget>[
                new IconButton(onPressed: (){
                  Widget addWidget = widget.addData();
                  if (addWidget == null) return;
                  Navigator.of(context).push(new MaterialPageRoute(
                      builder: (BuildContext buildContext) {
                        return addWidget;
                      }
                  ));
                }, icon: const Icon(Icons.add, color: Colors.blueAccent,), ),

                new IconButton(onPressed: (){
                  List<TableBeanVo> selectedDatas = _dessertsDataSource.selectedDatas;
                  if (selectedDatas != null && selectedDatas.length > 0) {
                    Widget editWidget = widget.editData(selectedDatas[0]);
                    if (editWidget == null) return;
                    Navigator.of(context).push(new MaterialPageRoute(
                        builder: (BuildContext buildContext) {
                          return editWidget;
                        }
                    ));
                  }
                }, icon: const Icon(Icons.edit, color: Colors.blueAccent), ),

                new IconButton(onPressed: (){}, icon: const Icon(Icons.delete, color: Colors.blueAccent,), ),
              ],
              )
        ]
    );
  }

  void _addData(List<TableBeanVo> tableBeanVoList) {
    _dessertsDataSource.setDatas(tableBeanVoList);
    setState(() {
      isLoaded = true;
    });
  }
}

class LoadingTableDataSource extends DataTableSource {

  LoadingTableDataSource(LoadingTable loadingTable, List<TableBeanVo> tableBeanList){

    this._loadingTable = loadingTable;
    this._tableBeanList = tableBeanList;
  }

  List<TableBeanVo> _tableBeanList = null;
  LoadingTable _loadingTable;

  void _sort<T>(Comparable<T> getField(TableBeanVo d), bool ascending) {
    _tableBeanList.sort((TableBeanVo a, TableBeanVo b) {
      if (!ascending) {
        final TableBeanVo c = a;
        a = b;
        b = c;
      }
      final Comparable<T> aValue = getField(a);
      final Comparable<T> bValue = getField(b);
      return Comparable.compare(aValue, bValue);
    });
    notifyListeners();
  }

  int _selectedCount = 0;

  @override
  DataRow getRow(int index) {
    assert(index >= 0);
    if (index >= _tableBeanList.length)
      return null;
    final TableBeanVo beanVo = _tableBeanList[index];
    return new DataRow.byIndex(
        index: index,
        selected: beanVo.selected,
        onSelectChanged: (bool value) {
          if (beanVo.selected != value) {
            _selectedCount += value ? 1 : -1;
            assert(_selectedCount >= 0);
            beanVo.selected = value;
            notifyListeners();
          }
        },
        cells: _loadingTable.celles(beanVo.bean).map<DataCell>((widget) {
          return new DataCell(widget);
        }).toList()
    );
  }

  @override
  int get rowCount => _tableBeanList.length;

  @override
  bool get isRowCountApproximate => false;

  @override
  int get selectedRowCount => _selectedCount;

  void _selectAll(bool checked) {
    for (TableBeanVo vo in _tableBeanList)
      vo.selected = checked;
    _selectedCount = checked ? _tableBeanList.length : 0;
    notifyListeners();
  }

  void setDatas(List<TableBeanVo> tableBeanVoList) {
    this._tableBeanList = tableBeanVoList;
//    notifyListeners();
  }

  List<TableBeanVo> get selectedDatas {
    List<TableBeanVo> selectedDatas = new List();
    for (TableBeanVo vo in _tableBeanList) {
      if (vo.selected) {
        selectedDatas.add(vo);
      }
    }
    return selectedDatas;
  }
}

class TableBeanVo<T> {

  TableBeanVo(this.bean, this.selected);

  T bean;
  bool selected = false;
}

typedef Comparable<T> GetField<T>(TableBeanVo tableBeanVo);

class SortDataColumn<T> extends DataColumn {

  SortDataColumn.sort({
    Widget label,
    String tooltip,
    bool numeric,
    DataColumnSortCallback onSort,
    this.getField,
  }): super(label: label, tooltip: tooltip, numeric: numeric, onSort: onSort);

  GetField<T> getField;

}